package ch.usi.inf.pf2.cell;

import ch.usi.inf.pf2.node.Addition;
import ch.usi.inf.pf2.node.Constant;
import ch.usi.inf.pf2.node.Division;
import ch.usi.inf.pf2.node.Multiplication;
import ch.usi.inf.pf2.node.Subtraction;
import ch.usi.inf.pf2.node.Node;
import ch.usi.inf.pf2.cell.TokenType;
/**
 * @author Matthias.Hauswirth@unisi.ch,Elham.MenzerNasr.Esfahani@usi.ch
 */
public final class Parser {

	private static Recognizer recog = new Recognizer();
	CellArray cellArray;

	public Parser(CellArray cellArray) {
		this.cellArray = cellArray;
	}
	/**
	 * Grammar:
	 * group       ::= OPEN_PARENTHESIS expression CLOSED_PARENTHESIS;
	 * a GROUP is an EXPRESSION inside parentheses
	 * 
	 * factor      ::= (CELL_REFERENCE | FLOATINGPOINTNUM | group);
	 * a FACTOR is either a CELL_REFERENCE or a FLOATINGPOINTNUM or a GROUP
	 * 
	 * term        ::= factor  ( (STAR | SLASH) factor )*;
	 * a TERM is a FACTOR, possibly connected to an arbitrary number of FACTORs using * or /
	 * 
	 * expression  ::= term ( (PLUS | MINUS) term )*
	 * an EXPRESSION is a TERM, possibly connected to an arbitrary number of TERMs using + or -
	 */
	public Node parseFormula(final String formula) {
		recog.setText(formula);
		recog.fetchNextToken();
		final Node expression = parseExpression();
		if (recog.getCurrentToken().getType()!=TokenType.END_OF_TEXT) {
			System.out.println("Syntax error: Superflous text starting at position "+recog.getCurrentToken().getStartPosition());
		}
		return expression;
	}
	/** 
	 * expression  ::= term ( (PLUS | MINUS) term )*
	 * an EXPRESSION is a TERM, possibly connected to an arbitrary number of TERMs using + or -
	 */
	private Node parseExpression() {
		// factor
		Node node = parseTerm();
		if (node==null) {
			return null;
		}
		while (recog.getCurrentToken().getType()==TokenType.PLUS || recog.getCurrentToken().getType()==TokenType.MINUS) {
			// consume '+' or '-' operator
			final TokenType operatorType = recog.getCurrentToken().getType();
			recog.fetchNextToken();
			// factor
			final Node right = parseTerm();
			if (right==null) {
				return null;
			}
			if (operatorType==TokenType.PLUS) {
				node = new Addition(node, right);
			} else {
				node = new Subtraction(node, right);
			}
		}
		return node;

	}

	/**
	 * term        ::= factor  ( (STAR | SLASH) factor )*;
	 * a TERM is a FACTOR, possibly connected to an arbitrary number of FACTORs using * or /
	 */
	private Node parseTerm() {
		// factor
		Node node = parseFactor();
		if (node==null) {
			return null;
		}
		while (recog.getCurrentToken().getType()==TokenType.STAR || recog.getCurrentToken().getType()==TokenType.SLASH) {
			// consume '*' or '/' operator
			final TokenType operatorType = recog.getCurrentToken().getType();
			recog.fetchNextToken();
			// factor
			final Node right = parseFactor();
			if (right==null) {
				return null;
			}
			if (operatorType==TokenType.STAR) {
				node = new Multiplication(node, right);
			} else {
				node = new Division(node, right);
			}
		}
		return node;

	}

	/**
	 * 
	 * group       ::= OPEN_PARENTHESIS expression CLOSED_PARENTHESIS;
	 *a GROUP is an EXPRESSION inside parentheses
	 */
	private Node parseGroup() {
		if (recog.getCurrentToken().getType() == TokenType.OPEN_PARENTHESIS) {
			recog.fetchNextToken();
		} else {
			return null;
		}
		Node node = parseExpression();
		if (recog.getCurrentToken().getType() == TokenType.CLOSED_PARENTHESIS) {
			recog.fetchNextToken();
		} else {
			return null;
		}
		return node;

	}

	/**
	 * factor      ::= (CELL_REFERENCE | FLOATINGPOINTNUM | group);
	 * a FACTOR is either a CELL_REFERENCE or a FLOATINGPOINTNUM or a GROUP
	 */
	public Node parseFactor() {
		if (recog.getCurrentToken().getType()==TokenType.FLOATINGPOINTNUM) {
			final double number = Double.parseDouble(recog.getCurrentToken().getText());
			recog.fetchNextToken();
			return new Constant(number);
			
		}else if (recog.getCurrentToken().getType()==TokenType.MINUS) {
			System.out.println("got a negative number");
			recog.fetchNextToken();
			final double number = Double.parseDouble(recog.getCurrentToken().getText());
			recog.fetchNextToken();
			return new Constant(-number);
			
		} else if (recog.getCurrentToken().getType()==TokenType.CELL_REFERENCE) {
			String ref = recog.getCurrentToken().getText();
			recog.fetchNextToken();
			int pos=0;
			int column = 0;
			int row = 0;
			while (pos<ref.length() && ref.charAt(pos)>='A' && ref.charAt(pos)<='Z') {
				column *= 26;
				column += (int)ref.charAt(pos)-(int)'A'+1;
				pos++;
			}
			while (pos<ref.length() && ref.charAt(pos)>='0' && ref.charAt(pos)<='9') {
				row *= 10;
				row += (int)ref.charAt(pos)-(int)'0';
				pos++;
			}
			row -= 1;
			
			return cellArray.getCell(row,column);
			
		} else if (recog.getCurrentToken().getType()==TokenType.OPEN_PARENTHESIS) {
			return parseGroup();
		} else {
			System.out.println("Syntax error: CELL_REFERENCE or FLOATINGPOINTNUM or OPEN_PARENTHESIS or NEGATIVENUM expected, but got '"+
					recog.getCurrentToken().getText()+"' at position "+
					recog.getCurrentToken().getStartPosition());
			return null;

		}
	}


}


